/*
* Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
*
* Author: zccrs <zccrs@live.com>
*
* Maintainer: zccrs <zhangjide@deepin.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QtWidgets>
#include "cplusplus/CppDocument.h"
#include "cplusplus/TranslationUnit.h"
#include "CppRewriter.h"
#include "cplusplus/AST.h"
#include "cplusplus/Parser.h"
#include "cplusplus/Bind.h"
#include <QStringList>
#include <cplusplus/ASTVisitor.h>
#include <QHeaderView>

using namespace CPlusPlus;

static QString symbolName(Symbol *symbol)
{
    if(nullptr == symbol)
        return "";
    const Identifier *objectId = symbol->identifier();
    if(nullptr == objectId)
        return "";
    return QByteArray(objectId->chars(), static_cast<int>(objectId->size()));
}

static const int roleFilePath = Qt::UserRole + 1;
static const int roleIsFile = Qt::UserRole + 2;


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("ASTAutoAddLog");

    m_treeModel = new QStandardItemModel(this);
    ui->treeView->setModel(m_treeModel);

    m_FuncModel = new QStandardItemModel(this);
    m_FuncModel->setHorizontalHeaderLabels(QStringList() << "funcName" << "argNameList" << "start" << "end");
    ui->tableView->setModel(m_FuncModel);
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);

    connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onOpenRootFloder);
    connect(ui->actioncollapseAll, &QAction::triggered, ui->treeView, &QTreeView::collapseAll);
    connect(ui->actionexpandAll, &QAction::triggered, ui->treeView, &QTreeView::expandAll);
    connect(ui->actionRun, &QAction::triggered, this, &MainWindow::onRun);
    connect(ui->actionExit, &QAction::triggered, this, &MainWindow::onExit);
    connect(ui->treeView, &QTreeView::clicked, this, &MainWindow::onSourceFileClick);

    resize(1100, 600);

    openFloder("/home/chz/Documents/git_code/konsole");
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::onOpenRootFloder()
{
    QString dir = QFileDialog::getExistingDirectory(this, "", "~");
    if(dir.isEmpty()) {
        return;
    }
    openFloder(dir);
}

void MainWindow::onSourceFileClick(const QModelIndex &index)
{
    auto createItem = [](const QString &text, bool isCheckable = false)->QStandardItem*{
        QStandardItem *item = new QStandardItem(text);
        item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
        if(isCheckable)
            item->setData(Qt::Checked, Qt::CheckStateRole);
        return item;
    };
    m_FuncModel->removeRows(0, m_FuncModel->rowCount());

    QString filepath = index.data(roleFilePath).toString();
    for(const FunctionInfo &info : getFuncInfo(filepath)) {
        if(info.funcName.trimmed().isEmpty())
            continue;
        m_FuncModel->appendRow(QList<QStandardItem*>()
                               << createItem(info.funcName)
                               << createItem(info.argNameList.join(","))
                               << createItem(QString::number(info.start))
                               << createItem(QString::number(info.end))
                               );
    }
    for(int column = 0; column < m_FuncModel->columnCount(); column ++) {
        ui->tableView->resizeColumnToContents(column);
        ui->tableView->setColumnWidth(column, ui->tableView->columnWidth(column) + 50);
    }
}

void MainWindow::onRun()
{
    //备份根目录文件夹
    backupRootDirectory();
    //获取所有选择的文件
    QStringList checkedFiles = getCheckedFiles(m_treeModel->invisibleRootItem());
    foreach(const QString &filepath, checkedFiles) {
       QList<FunctionInfo> infos = getFuncInfo(filepath);
       //在文件头添加debug头文件
       prependDebugLink(filepath);
       //获取源码
       QByteArray content = readFile(filepath);
       //从大到小遍历，把log相关代码写入源码中
       for(int k = infos.count() - 1; k >= 0; k --) {
           insertFile(content, infos[k]);
       }
       //保存
       writeFile(content, filepath);
    }
}

void MainWindow::onExit()
{
    this->close();
}

void MainWindow::createTreeNode(const QString &root, const QString &dir)
{
    if(fileCount(dir) == 0)
        return;
    createFilePathNode(root, dir);
    foreach(const QFileInfo &info, QDir(dir).entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDir::DirsFirst)) {
        if(info.isFile()){
            if(info.fileName().endsWith(".cpp")
                    || info.fileName().endsWith(".c")) {
                createFilePathNode(root, info.filePath());
            }
        }
        if(info.isDir()) {
            createTreeNode(root, info.filePath());
        }
    }
}

void MainWindow::createFilePathNode(const QString &root, const QString &filepath)
{
    QStringList list = filepath.mid(root.size()).split("/");
    QStandardItem *parentItem = m_treeModel->invisibleRootItem();
    for(int index = 0; index < list.count(); index ++) {
        bool finded = false;
        for(int row = 0; row < parentItem->rowCount(); row ++) {
            if(parentItem->child(row, 0)->text() == list[index]) {
                parentItem = parentItem->child(row, 0);
                finded = true;
                break;
            }
        }
        if(!finded) {
            QStandardItem *item = new QStandardItem;
            item->setText(list[index]);
            item->setData(filepath, roleFilePath);
            item->setData(QFileInfo(filepath).isFile(), roleIsFile);
            item->setData(Qt::Checked, Qt::CheckStateRole);
            item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
            parentItem->appendRow(item);
            parentItem = item;
        }
    }
}

QList<FunctionInfo> MainWindow::getFuncInfo(const QString &filepath)
{
    QList<FunctionInfo> list;
    //判断是否为文件
    if(!QFileInfo(filepath).isFile())
        return list;

    QFile file(filepath);
    if(!file.open(QFile::ReadOnly))
        return list;

    QByteArray all = file.readAll();

    Document::Ptr doc = Document::create(filepath);
    //导入数据
    doc->setUtf8Source(all);
    //解析
    if(!doc->parse())
        return list;
    doc->check();
    //解析指针
    ASTVisitor visitor(doc->translationUnit());

    //遍历所有的symbol
    for(uint k = 0, count = doc->globalSymbolCount(); k < count; k ++)
    {
        //symbol 是函数时
        if(doc->globalSymbolAt(k)->isFunction()) {
            list.append(FunctionInfo());
            //获取函数体信息
            Function *func = doc->globalSymbolAt(k)->asFunction();
            //获取函数名
            list.last().funcName = symbolName(doc->globalSymbolAt(k));
            //获取函数参数
            for(uint k = 0, count = func->argumentCount(); k < count; k ++)
            {
                list.last().argNameList << symbolName(func->argumentAt(k));
            }
            //获取函数开始行和结束行
            uint line_start;
            uint line_end;
            visitor.getPosition(func->startOffset(), &line_start);
            visitor.getPosition(func->endOffset(), &line_end);
            list.last().start = line_start;
            list.last().end = line_end;
        }
    }
    return list;
}

void MainWindow::openFloder(const QString &dir)
{
    m_treeModel->clear();
    m_treeModel->setHorizontalHeaderItem(0, new QStandardItem(dir));

    //遍历生成item
    createTreeNode(dir, dir);

    //设置root item
    QStandardItem *item = m_treeModel->item(0, 0);
    if(item)
        item->setText(QFileInfo(dir).fileName());
    ui->treeView->expandAll();
    ui->tableView->resizeColumnToContents(0);
}

QByteArray MainWindow::readFile(const QString &filepath)
{
    return "";
}

void MainWindow::insertFile(QByteArray &source, int line, const QByteArray &content)
{

}

void MainWindow::insertFile(QByteArray &source, FunctionInfo info)
{

}

void MainWindow::writeFile(QByteArray &source, const QString &filepath)
{

}

void MainWindow::backupRootDirectory()
{

}

void MainWindow::prependDebugLink(const QString &filepath)
{

}

int MainWindow::fileCount(const QString &dir)
{
    int count = 0;
    foreach(const QFileInfo &info, QDir(dir).entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDir::DirsFirst)) {
        if(info.isFile()){
            if(info.fileName().endsWith(".cpp")
                    || info.fileName().endsWith(".c")) {
                count ++;
            }
        }
        if(info.isDir()) {
            count += fileCount(info.filePath());
        }
    }
    return count;
}

QStringList MainWindow::getCheckedFiles(QStandardItem *item)
{
    QStringList fileList;
    for(int row = 0; row < item->rowCount(); row ++) {
        QStandardItem *child = item->child(row);
        if(child->data(roleIsFile).toBool()) {
            if(child->data(Qt::CheckStateRole).toInt() == Qt::Checked){
                fileList << child->data(roleFilePath).toString();
            }
        } else {
            fileList << getCheckedFiles(child);
        }
    }
    return fileList;
}
